package start;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.GregorianCalendar;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class Main {
    public static void main(String[] args) throws NoSuchMethodException {
        {
            ApplicationContext context;
            ExpressionParser parser = new SpelExpressionParser();
            Expression exp = parser.parseExpression("'Hello World'.concat('!')");
            String message = (String)exp.getValue();
            System.out.println(message);

            // invokes 'getBytes().length'
            Expression exp2 = parser.parseExpression("'Hello World'.bytes.length");
            int length = (Integer)exp2.getValue();
            System.out.println(length);

            GregorianCalendar c = new GregorianCalendar();
            c.set(1856, 7, 9);
            //  The constructor arguments are name, birthday, and nationality.
            Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
            ExpressionParser parser2 = new SpelExpressionParser();
            Expression exp3 = parser2.parseExpression("name");
            EvaluationContext eContext = new StandardEvaluationContext(tesla);
            String name = (String)exp3.getValue(eContext);
            System.out.println(name);
            Expression exp4 = parser2.parseExpression("name == 'Nikola Tesla'");
            boolean result = exp4.getValue(eContext, Boolean.class); // evaluates to true
            System.out.println(result);

            ExpressionParser parser3 = new SpelExpressionParser();
            Expression exp5 = parser3.parseExpression("-1*(1+2*5)");
            Integer res = exp5.getValue(Integer.class);
            System.out.println(res);
        }
        //7.4.1 XML based configuration
        ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF\\beans.xml");
        A a = ctx.getBean(A.class);
        System.out.println(a.getRandomNumber());
        System.out.println(a.getLocale());
        System.out.println(a.getProperties());
        B b = ctx.getBean(B.class);
        System.out.println(b.getRandomNumber());

        //7.5 Language Reference
        {
            ExpressionParser parser = new SpelExpressionParser();
            //7.5.1 Literal expressions
            System.out.println((String)parser.parseExpression("'Hello World'").getValue());
            System.out.println((Double)parser.parseExpression("6.0221415E+23").getValue());
            System.out.println((Integer)parser.parseExpression("0x7FFFFFFF").getValue());
            System.out.println((Boolean)parser.parseExpression("trUe").getValue());
            System.out.println(parser.parseExpression("null").getValue());
            //7.5.2 Properties, Arrays, Lists, Maps, Indexers
            GregorianCalendar c = new GregorianCalendar();
            c.set(1856, 7, 9);
            Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
            EvaluationContext context = new StandardEvaluationContext(tesla);
            int year = (Integer)parser.parseExpression("Birthdate.Year + 1900").getValue(context);
            System.out.println(year);
            //7.5.3 Inline lists
            List numbers = (List)parser.parseExpression("{1,2,3,4}").getValue();
            System.out.println(numbers);
            List listOfLists = (List)parser.parseExpression("{{'a','b'},{'x','y'}}").getValue();
            System.out.println(listOfLists);
            //7.5.4 Array construction
            int[] numbers1 = (int[])parser.parseExpression("new int[4]").getValue();
            System.out.println(numbers1);
            int[] numbers2 = (int[])parser.parseExpression("new int[]{1,2,3}").getValue();
            System.out.println(numbers2);
            int[][] numbers3 = (int[][])parser.parseExpression("new int[4][5]").getValue();
            System.out.println(numbers3);
            //7.5.5 Methods
            String c2 = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);
            System.out.println(c2);
            //7.5.6 Operators
            boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);
            System.out.println(trueValue);
            boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
            System.out.println(falseValue);
            boolean trueValue2 = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
            System.out.println(trueValue2);
            boolean falseValue2 = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);
            boolean trueValue3 =
                parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
            boolean falseValue3 =
                parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
            boolean falseValue4 = parser.parseExpression("true and false").getValue(Boolean.class);
            //String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
            //boolean trueValue4 = parser.parseExpression(expression).getValue(Boolean.class);
            //7.5.7 Assignment
            Inventor inventor = new Inventor();
            StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor);
            parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2");
            String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inventorContext, String.class);
            System.out.println(aleks);
            //7.5.8 Types
            Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
            System.out.println(dateClass);
            Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
            System.out.println(stringClass);
            boolean trueValue4 =
                parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class);
            System.out.println(trueValue4);
            //7.5.9 Constructors
            Inventor einstein =
                parser.parseExpression("new start.Inventor('Albert Einstein','German')").getValue(Inventor.class);
            System.out.println(einstein);
            //7.5.10 Variables
            Inventor tesla2 = new Inventor("Nikola Tesla", "Serbian");
            StandardEvaluationContext context2 = new StandardEvaluationContext(tesla2);
            context2.setVariable("newName", "Mike Tesla");
            parser.parseExpression("Name = #newName").getValue(context2);
            System.out.println(tesla2.getName()); // "Mike Tesla"
            //7.5.10.1 The #this and #root variables
            List<Integer> primes = new ArrayList<Integer>();
            primes.addAll(Arrays.asList(2, 3, 5, 7, 11, 13, 17));
            context2.setVariable("primes", primes);
            List<Integer> primesGreaterThanTen =
                (List<Integer>)parser.parseExpression("#primes.?[#this>10]").getValue(context2);
            System.out.println(primesGreaterThanTen);
            //7.5.11 Functions
            context2.registerFunction("reverseString",
                                      StringUtils.class.getDeclaredMethod("reverseString", new Class[] { String.class }));
            String helloWorldReversed =
                parser.parseExpression("#reverseString('hello')").getValue(context2, String.class);
            System.out.println(helloWorldReversed);
            //7.5.12 Bean references
            context2.setBeanResolver(new BeanResolver() {
                    @Override
                    public Object resolve(EvaluationContext context, String beanName) {
                        System.out.println("BeanResolver resolve("+context+","+beanName+")");
                        return beanName;
                    }
                });
            Object bean = parser.parseExpression("@foo").getValue(context2);
            System.out.println(bean);
            //7.5.13 Ternary Operator (If-Then-Else)
            String falseString = 
                         parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);
            System.out.println(falseString);
            //7.5.14 The Elvis Operator
            String name = "Elvis Presley";
            String displayName = name != null ? name : "Unknown";
            //7.5.15 Safe Navigation operator
            String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context2, String.class);
            System.out.println(city);
            //7.5.16 Collection Selection
            List<B> bList = new ArrayList<B>();
            bList.addAll(Arrays.asList(new B(7), new B(23),new B(11)));
            System.out.println(bList);
            context2.setVariable("bList", bList);
            List<Integer> selec =
                (List<Integer>)parser.parseExpression("#bList.?[randomNumber>10]").getValue(context2);
            System.out.println(selec);
            //7.5.17 Collection Projection
                // returns [ 'Smiljan', 'Idvor' ]
            //List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");
            //System.out.println(placesOfBirth);
            //7.5.18 Expression templating
            String randomPhrase = 
               parser.parseExpression("random number is #{T(java.lang.Math).random()}", 
                                      new TemplateParserContext()).getValue(String.class);
            System.out.println(randomPhrase);
            
        }
    }
}
